package com.etc.aop;

import com.alibaba.fastjson.JSON;
import com.etc.common.RateLimit;
import com.etc.common.ResultBody;
import com.etc.common.ResultStatusEnums;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @Author kalista
 * @Description
 * @Date 2021/2/26  16:27
 **/
@Component
@Aspect
@Slf4j
public class RateLimitAspect {


    //  这样一个并发容          接口     令牌同
    private ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>();

    @Autowired
    private HttpServletResponse response;

    /**
     * 通知
     * 前置通知
     * 后置通知
     * 环绕通知
     * 异常通知
     * 返回通知
     */

    private static final String POINT = "execution(* com.etc.controller.*.*(..))";


    @Pointcut(POINT)
    private void serviceLimit() {

    }

    private RateLimiter rateLimiter;


    @Around("execution(* com.etc.controller.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object object = null;
        // 1. 获取拦截方法的名字 获取代理类信息
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        // 2. 处理目标对象
        Object target = joinPoint.getTarget();
        // 3. 获取目标对象的方法信息
        Method method = target.getClass().getMethod(signature.getName(), methodSignature.getParameterTypes());
        // 4. 判断方法是否加 RateLimit 注解
        if (method.isAnnotationPresent(RateLimit.class)) {
            // 5. 获取具体注解
            RateLimit rateLimit = method.getAnnotation(RateLimit.class);
            // 6. 获取注解的信息
            int limitNum = rateLimit.limitNum();
            // 7. 验证limitnum数量
            checkLimitNum(limitNum);
            // 8. 获取接口的名字
            String funcationName = target.getClass().getSimpleName() + methodSignature.getName();
            // 9. 判断map种是否包含 接口名字的key
            if (map.containsKey(funcationName)) {
                rateLimiter = map.get(funcationName);
            } else {
                RateLimiter rateLimiter = RateLimiter.create(limitNum);
                map.put(funcationName, rateLimiter);
                this.rateLimiter = rateLimiter;
            }
            try {
                // 等于true 获取到了令牌
                if (rateLimiter.tryAcquire(500, TimeUnit.MICROSECONDS)) {
                    object = joinPoint.proceed();
                } else {
                    render();
                }
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }

            return object;
        } else {
            object = joinPoint.proceed();
            return object;
        }

    }

    private void render() throws IOException {
        response.setContentType("application/json; charset=UTF-8");
        OutputStream outputStream = response.getOutputStream();
        String str = JSON.toJSONString(ResultBody.error(ResultStatusEnums.TAIMANG));
        outputStream.write(str.getBytes("UTF-8"));
        outputStream.flush();
        outputStream.close();
    }


    private void checkLimitNum(Integer limitNum) throws Exception {
        if (limitNum <= 0) {
            throw new Exception("注解错误");
        }

    }

}
